package com.github.prontera.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.List;

public class AnnotationUtils {

	/**
	 * Retrieve the <em>value</em> of the {@code &quot;value&quot;} attribute of
	 * a single-element Annotation, given an annotation instance.
	 * 
	 * @param annotation
	 *            the annotation instance from which to retrieve the value
	 * @return the attribute value, or {@code null} if not found
	 * @see #getValue(Annotation, String)
	 */
	public static Object getValue(Annotation annotation) {
		return org.springframework.core.annotation.AnnotationUtils.getValue(annotation);
	}

	/**
	 * Retrieve the <em>value</em> of a named Annotation attribute, given an
	 * annotation instance.
	 * 
	 * @param annotation
	 *            the annotation instance from which to retrieve the value
	 * @param attributeName
	 *            the name of the attribute value to retrieve
	 * @return the attribute value, or {@code null} if not found
	 * @see #getValue(Annotation)
	 */
	public static Object getValue(Annotation annotation, String attributeName) {
		return org.springframework.core.annotation.AnnotationUtils.getValue(annotation, attributeName);
	}

	/**
	 * Get all {@link Annotation Annotations} from the supplied {@link Method}.
	 * <p>
	 * Correctly handles bridge {@link Method Methods} generated by the
	 * compiler.
	 * 
	 * @param method
	 *            the method to look for annotations on
	 * @return the annotations found
	 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)
	 */
	public static Annotation[] getAnnotations(Method method) {
		return org.springframework.core.annotation.AnnotationUtils.getAnnotations(method);
	}

	/**
	 * Get a single {@link Annotation} of {@code annotationType} from the
	 * supplied Method, Constructor or Field. Meta-annotations will be searched
	 * if the annotation is not declared locally on the supplied element.
	 * 
	 * @param ae
	 *            the Method, Constructor or Field from which to get the
	 *            annotation
	 * @param annotationType
	 *            the annotation class to look for, both locally and as a
	 *            meta-annotation
	 * @return the matching annotation or {@code null} if not found
	 * @since 3.1
	 */
	public static <T extends Annotation> T getAnnotation(AnnotatedElement ae, Class<T> annotationType) {
		return org.springframework.core.annotation.AnnotationUtils.getAnnotation(ae, annotationType);
	}

	/**
	 * Find a single {@link Annotation} of {@code annotationType} from the
	 * supplied {@link Class}, traversing its interfaces and superclasses if no
	 * annotation can be found on the given class itself.
	 * <p>
	 * This method explicitly handles class-level annotations which are not
	 * declared as {@link java.lang.annotation.Inherited inherited} <i>as well
	 * as annotations on interfaces</i>.
	 * <p>
	 * The algorithm operates as follows: Searches for an annotation on the
	 * given class and returns it if found. Else searches all interfaces that
	 * the given class declares, returning the annotation from the first
	 * matching candidate, if any. Else proceeds with introspection of the
	 * superclass of the given class, checking the superclass itself; if no
	 * annotation found there, proceeds with the interfaces that the superclass
	 * declares. Recursing up through the entire superclass hierarchy if no
	 * match is found.
	 * 
	 * @param clazz
	 *            the class to look for annotations on
	 * @param annotationType
	 *            the annotation class to look for
	 * @return the annotation found, or {@code null} if none found
	 */
	public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType) {
		return org.springframework.core.annotation.AnnotationUtils.findAnnotation(clazz, annotationType);
	}

	/**
	 * Get a single {@link Annotation} of {@code annotationType} from the
	 * supplied {@link Method}, traversing its super methods if no annotation
	 * can be found on the given method itself.
	 * <p>
	 * Annotations on methods are not inherited by default, so we need to handle
	 * this explicitly.
	 * 
	 * @param method
	 *            the method to look for annotations on
	 * @param annotationType
	 *            the annotation class to look for
	 * @return the annotation found, or {@code null} if none found
	 */
	public static <A extends Annotation> A findAnnotation(Method method, Class<A> annotationType) {
		return org.springframework.core.annotation.AnnotationUtils.findAnnotation(method, annotationType);
	}

	/**
	 * Find the first {@link Class} in the inheritance hierarchy of the
	 * specified {@code clazz} (including the specified {@code clazz} itself)
	 * which declares an annotation for the specified {@code annotationType}, or
	 * {@code null} if not found. If the supplied {@code clazz} is {@code null},
	 * {@code null} will be returned.
	 * <p>
	 * If the supplied {@code clazz} is an interface, only the interface itself
	 * will be checked; the inheritance hierarchy for interfaces will not be
	 * traversed.
	 * <p>
	 * The standard {@link Class} API does not provide a mechanism for
	 * determining which class in an inheritance hierarchy actually declares an
	 * {@link Annotation}, so we need to handle this explicitly.
	 * 
	 * @param annotationType
	 *            the Class object corresponding to the annotation type
	 * @param clazz
	 *            the Class object corresponding to the class on which to check
	 *            for the annotation, or {@code null}
	 * @return the first {@link Class} in the inheritance hierarchy of the
	 *         specified {@code clazz} which declares an annotation for the
	 *         specified {@code annotationType}, or {@code null} if not found
	 * @see Class#isAnnotationPresent(Class)
	 * @see Class#getDeclaredAnnotations()
	 * @see #findAnnotationDeclaringClassForTypes(List, Class)
	 * @see #isAnnotationDeclaredLocally(Class, Class)
	 */
	public static Class<?> findAnnotationDeclaringClass(Class<? extends Annotation> annotationType, Class<?> clazz) {
		return org.springframework.core.annotation.AnnotationUtils.findAnnotationDeclaringClass(annotationType, clazz);
	}
}
